`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2023/12/02 14:50:56
// Design Name: 
// Module Name: rv_thread
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
module rv_thread(
    input CLK,input RST,
    //EXTERN --> rv_pc
    // 1:running 0:CLR
    input EBUS_RV_RUN,
    //IMEM-->rv_thread
    input [31:0] INST,   
    //rv_thread-->PC unit
    output THREAD_ID_FOR_PC_VALID,
    output [1:0] THREAD_ID_FOR_PC,

    //rv_thread-->decoder
    output ISSUE_THREAD_RUN,
    output [1:0] ISSUE_THREAD_ID,

    //hardware sig
    input [14:0] SIGHARD,
    //rv_thread_sig-->rv_thread
    input [15:0] SIGSOFT,

    //rv_wait_para-->rv_thread
    input [4*32-1:0] ALL_WAIT_PARA

);
    reg if_thread_running_din;
    wire if_thread_running;
    regw #(.WIDTH(1)) IF_RUNNING_REG(CLK,RST,1'b1,if_thread_running_din,if_thread_running);
    assign THREAD_ID_FOR_PC_VALID=if_thread_running_din;
    assign ISSUE_THREAD_RUN=if_thread_running;

    reg [1:0] if_thread_id_din;
    wire [1:0] if_thread_id;
    regw #(.WIDTH(2)) IF_THREAD_ID_REG(CLK,RST,1'b1,if_thread_id_din,if_thread_id);
    assign THREAD_ID_FOR_PC=if_thread_id_din;
    assign ISSUE_THREAD_ID=if_thread_id;

    wire [3:0] thread_cs1,thread_cs;
    decoder2to4 THREAD_CS_DECODER(if_thread_id,thread_cs1);
    assign thread_cs=thread_cs1&{4{if_thread_running}};
    //00: Normal inst; 01:Update PC inst; 10:Load inst; 11:WAIT inst
    reg [1:0] inst_type_thread;
    always @(*) begin
        if((INST==32'b1000_0000_0000_00001_110_00000_1110011)&(if_thread_running==1'b1)) inst_type_thread=2'b11;
        else if((INST[6:2]==5'b00_000)&(if_thread_running==1'b1)) inst_type_thread=2'b10;
        else if(((INST[6:2]==5'b11_000)|(INST[6:2]==5'b11_001)|(INST[6:2]==5'b11_011))&(if_thread_running==1'b1))
            inst_type_thread=2'b01;
        else inst_type_thread=2'b00;
    end
    wire [`THREAD_NUM-1:0] sig_satisfied,can_run,blocked;
    genvar i;
    generate
        for(i=0;i<`THREAD_NUM;i=i+1)begin
            rv_thread_fsm THREAD_FSM(CLK,RST,EBUS_RV_RUN,thread_cs[i],
                inst_type_thread,sig_satisfied[i],
                can_run[i],blocked[i]);
            sig_check SIG_CHECK(ALL_WAIT_PARA[i*32+31:i*32],
                {SIGHARD,SIGSOFT},sig_satisfied[i]);
        end
    endgenerate
    //Next thread except me
    wire next_thread_id_valid;
    wire [1:0] next_thread_id;
    next_thread NEXT_THREAD(if_thread_running,
        if_thread_id,can_run,next_thread_id_valid,next_thread_id);

    //While cur thread satified the sigs
    wire cur_thread_sig_satisfied;
    assign cur_thread_sig_satisfied=|(if_thread_id&sig_satisfied);

    always @(*)begin
        if(if_thread_running==1'b0) begin
            if_thread_running_din=next_thread_id_valid;
            if_thread_id_din=next_thread_id;
        end
        else begin
            if((inst_type_thread==2'b00)|
                ((inst_type_thread==2'b11)&(cur_thread_sig_satisfied==1'b1))) begin
                    if_thread_running_din=if_thread_running;
                    if_thread_id_din=if_thread_id;
                end
            else begin
                if_thread_running_din=next_thread_id_valid;
                if_thread_id_din=next_thread_id;
                end
        end
    end
        
    
endmodule

module rv_thread_fsm(
    input CLK,input RST,
        //EXTERN --> rv_pc
    // 1:running 0:CLR
    input EBUS_RV_RUN,
    input THREAD_CS,
    //00: Normal inst; 01:Update PC inst; 10:Load inst; 11:WAIT inst
    input [1:0] INST_TYPE_THREAD,
    input SIG_SATISFIED,
    output CAN_RUN,
    output BLOCKED
    );

    localparam INIT = 3'b0;
    localparam ACTIVE = 3'b1 ;
    localparam WAIT3 = 3'd2 ;
    localparam WAIT2 = 3'd3 ;
    localparam WAIT1 = 3'd4;
    localparam WAIT_SIG = 3'd5 ;
    
    reg[2:0] state;
    always @(posedge CLK) begin
        if(RST) state<=INIT;
        else begin
            case(state)
                INIT: if(EBUS_RV_RUN==1'b0) state<=INIT;
                        else state<=ACTIVE;
                ACTIVE: if(EBUS_RV_RUN==1'b0) state<=INIT;
                        else begin 
                            if(THREAD_CS==1'b0) state<=ACTIVE;
                                else begin
                                    case(INST_TYPE_THREAD)
                                        2'b00: state<=ACTIVE;   //Normal inst
                                        2'b01: state<=WAIT3;    //Update PC inst
                                        2'b10: state<=WAIT1;    //Load inst
                                        2'b11: begin            //WAIT inst
                                            if(SIG_SATISFIED==1'b1) state<=ACTIVE;
                                            else state<=WAIT_SIG;
                                            end
                                    endcase 
                                end
                            end
                WAIT3: if(EBUS_RV_RUN==1'b0) state<=INIT;
                        else state<=WAIT2;
                WAIT2: if(EBUS_RV_RUN==1'b0) state<=INIT;
                        else state<=WAIT1;
                WAIT1: if(EBUS_RV_RUN==1'b0) state<=INIT;
                        else state<=ACTIVE;
                WAIT_SIG:   if(EBUS_RV_RUN==1'b0) state<=INIT;
                            else 
                                begin if(SIG_SATISFIED==1'b1) state<=ACTIVE;
                                            else state<=WAIT_SIG;
                                end
                default: state<=INIT;
            endcase
        end
    end
    assign CAN_RUN=(state==ACTIVE)?1'b1:1'b0;
    assign BLOCKED=(state==WAIT_SIG)?1'b1:1'b0;
endmodule

module sig_check(input [31:0] SIG_PARA,
    input [30:0] THREAD_SIG,
    output reg SATISFIED);
reg [30:0] temp;
always @(*) begin
    if(SIG_PARA[31]==1'b0) temp=(~SIG_PARA[30:0])|THREAD_SIG;
    else temp=SIG_PARA[30:0]&THREAD_SIG;
end
always @(*) begin
    if(SIG_PARA[31]==1'b0) begin
        if(temp==31'h8fff) SATISFIED=1'b1;
        else SATISFIED=1'b0;
    end
    else begin
        if(temp==31'b0)  SATISFIED=1'b1;
        else SATISFIED=1'b0;
    end
end
endmodule

module next_thread(input THREAD_RUNNING,
    input [1:0] THREAD_ID,
    input [3:0] CAN_RUN,
    output NEXT_THREAD_RUNNING,
    output reg [1:0] NEXT_THREAD_ID);

    wire [1:0] shf_num;
    wire [3:0] running;
    assign shf_num=(THREAD_RUNNING==1'b1)?THREAD_ID:2'b00;
    barrier_ror_shift4 RUN_ROR_SHIFTER(CAN_RUN,shf_num,running);
    wire [3:0] sel_thread0,sel_thread;
    Pencoder4to4 PENCODER({running[3:1],1'b0},sel_thread0,NEXT_THREAD_RUNNING);
    barrier_rol_shift4 RUN_ROL_SHIFTER(sel_thread0,shf_num,sel_thread);
    always @(*) begin
        case(sel_thread)
            4'b0001: NEXT_THREAD_ID=2'b00;
            4'b0010: NEXT_THREAD_ID=2'b01;
            4'b0100: NEXT_THREAD_ID=2'b10;
            4'b1000: NEXT_THREAD_ID=2'b11;
            default: NEXT_THREAD_ID=2'b00;
        endcase
    end
    
endmodule

module csr_thread_sig(
    input CLK,input RST,
    //Decode-->CSR
    input [2:0] FUNC3,
    input [4:0] RS1_INDEX,
    input [4:0] RD_INDEX,

    //Decode-->EX Data
    input [31:0] IMM32,
    
    //EX --> CSR
    input [31:0] RS1_FORWARD,

    //CSR controller --> ALL CSR modules
    input [11:0] CSR_ADDR,

    //CSR controller-->CSR_SIG
    input CSR_SIG_CS,
    //CSR_SIG --> CSR controller
    output  [31:0] CSR_SIG_DOUT

    //Other handware sigs
    
);

wire [15:0] sigsoft_init;
assign sigsoft_init=16'hfffe;
wire [15:0] sigsoft_dout;

reg [15:0] sigsoft_en,sigsoft_din;
always @(*) begin
    if((CSR_SIG_CS==1'b0)&(RS1_INDEX==5'b0)) begin
        sigsoft_en=16'b0;sigsoft_din=16'b0;
    end
    else begin
        case(FUNC3)
            `CSRRW: begin 
                sigsoft_en=16'hffff;sigsoft_din=RS1_FORWARD[15:0];
            end
            `CSRRS: begin 
                sigsoft_en=RS1_FORWARD[15:0];sigsoft_din=16'hffff;
            end
            `CSRRC: begin 
                sigsoft_en=RS1_FORWARD[15:0];sigsoft_din=16'h0000;
                end
            `CSRRWI:begin 
                sigsoft_en=16'hffff;sigsoft_din=IMM32[15:0];
            end
            `CSRRSI: begin 
            sigsoft_en=IMM32[15:0];sigsoft_din=16'hffff;
                end
            `CSRRCI: begin 
                sigsoft_en=IMM32[15:0];sigsoft_din=16'h0000;
                end
            default: begin 
                sigsoft_en=16'h0000;sigsoft_din=16'h0000;
                end
        endcase
    end
end

genvar i;
generate
    for(i=0;i<16;i=i+1) begin
        reg_initw #(.WIDTH(1)) SIGSOFT_REG(CLK,RST,
        sigsoft_en[i],sigsoft_init[i],sigsoft_din[i],sigsoft_dout[i]);
    end
endgenerate
    assign CSR_SIG_DOUT={16'b0,sigsoft_dout};
endmodule

module csr_wait_para(
    input CLK,input RST,
    //Decode-->CSR
    input [2:0] FUNC3,
    input [4:0] RS1_INDEX,
    input [4:0] RD_INDEX,

    //Decode-->EX Data
    input [31:0] IMM32,
    
    //EX --> CSR
    input [31:0] RS1_FORWARD,

    //CSR controller --> ALL CSR modules
    input [11:0] CSR_ADDR,

    //CSR controller-->CSR_WAIT_PARA
    input CSR_WAIT_PARA_CS,
    //CSR_WAIT_PARA --> CSR controller
    output  [31:0] CSR_WAIT_PARA_DOUT,
    
    //EX THREAD_ID
    input [1:0] THREAD_ID,
    //rv_wait_para-->rv_thread
    output [4*32-1:0] ALL_WAIT_PARA
);
    wire wait_para_en1;
    wire [3:0] wait_para_en;
    wire [3:0] thread_cs;
    wire [31:0] wait_para_din;

    csr_reg_wr_en WAIT_PARA_EN(CSR_WAIT_PARA_CS,
        FUNC3,RS1_INDEX,wait_para_en1);
    decoder2to4 THREAD_DECODER(THREAD_ID,thread_cs);
    assign wait_para={4{wait_para_en1}}&thread_cs;

    wire [31:0] cur_wait_para;
    csr_next_value WAIT_PARA_NEXT_VALUE(
        cur_wait_para,
        FUNC3,
        RS1_FORWARD,
        IMM32,
        wait_para_din
    );

    wire [4*32-1:0] wait_para_dout;
    genvar i;
    generate
        for(i=0;i<4;i=i+1) begin
            regw #(.WIDTH(32)) WAIT_PARA_REG(CLK,RST,wait_para_en[i],
                wait_para_din,wait_para_dout[i*32+31:i*32]);
        end
    endgenerate
    mux4w #(.WIDTH(32)) CSR_WAIT_PARA_DOUT_MUX(
        wait_para_dout[0*32+31:0*32],wait_para_dout[1*32+31:1*32],
        wait_para_dout[2*32+31:2*32],wait_para_dout[3*32+31:3*32],
        THREAD_ID,cur_wait_para);
    assign CSR_WAIT_PARA_DOUT=cur_wait_para;
    assign ALL_WAIT_PARA=wait_para_dout;
endmodule